#ifndef MACROS_H_
#define MACROS_H_

#include <iomanip>

#if CHECK_TYPES
    #define CHECK(cond, except) \
        if (!(cond)) \
            throw std::runtime_error(except); \
        else
#else
    #define CHECK(cond, except)
#endif

#if INSTRUCTION_TRACING
    #define INSTR_TRACE(instr, msg) do { std::cerr << std::right << std::setw(8) << instr << ' ' << msg << '\n'; } while (0)
#else
    #define INSTR_TRACE(instr, msg)
#endif

#if CALL_TRACING || INSTRUCTION_TRACING
    #if INSTRUCTION_TRACING
        #define CALL_TRACE(instr, msg) INSTR_TRACE(instr, msg)
        #define RET_TRACE(instr, msg) INSTR_TRACE(instr, msg)
        #define ERET_TRACE(instr, msg)
    #else
        static int trace_indent = 0, trace_last_indent = 0;
        #define CALL_TRACE(instr, msg) \
            do { \
                for(int trace_i = 0; trace_i < trace_indent; ++trace_i) \
                    std::cerr << "|   "; \
                std::cerr << msg << '\n'; \
                trace_last_indent = trace_indent++; \
            } while (0)
        #define RET_TRACE(instr, msg) \
            do { \
                --trace_indent; \
                if (trace_last_indent != trace_indent) { \
                    for(int trace_i = 0; trace_i < trace_indent; ++trace_i) \
                        std::cerr << "|   "; \
                    std::cerr << "+\n"; \
                } \
            } while (0)
        #define ERET_TRACE(instr, msg) do { --trace_indent; } while (0)
    #endif
#else
    #define CALL_TRACE(instr, msg)
    #define RET_TRACE(instr, msg)
    #define ERET_TRACE(instr, msg)
#endif

// makra pro citelnejsi pristup ke stacku
// TOS = Top Of Stack
#define TOS (sp)

// pointery na jiz napushovane hodnoty
#define TOS1 (TOS - 1)
#define TOS2 (TOS - 2)
#define TOS3 (TOS - 3)

// nekdy je rychlejsi posunout sp jako prvni krok
// a pak se odkazovat na jakoby smazane prvky stacku
#define TOSP1 (TOS + 1)
#define TOSP2 (TOS + 2)
#define TOSP3 (TOS + 3)

// makro na zapis komutativnich binarnich operaci
#define BOP_COMMUTATIVE(op) \
    do { TOS1->value op TOS->value; \
        --sp; } while (0)

// makro na zapis nekomutativnich binarnich operaci
#define BOP(op) \
    do { TOS->value = TOSP1->value op TOS->value; } while (0)

#define BOP_TRACE(instr) INSTR_TRACE(instr, *TOS << ' ' << *TOS1)

// makro na zapis porovnani
#define CMP_STACK_TOP(op) \
    do { TOS->value = ((TOSP1->value op TOS->value) ? 1 : 0) ; } while (0)

// End-Of-Heap
#define EOHCHECK \
    if(ref_table_ptr == obj_table_end) \
        throw std::runtime_error("Plna pamet!");

#endif /* MACROS_H_ */
